package com.acmen.easyapi.strategy.impl;

import com.acmen.easyapi.factory.ReqParamFactory;
import com.acmen.easyapi.strategy.ReqParamStrategy;
import com.acmen.easyapi.annotation.Param;
import com.acmen.easyapi.annotation.ReqParam;
import com.acmen.easyapi.util.ClassUtil;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @author acmen
 * 策略模式
 */
@Component
public class ReqParamStrategyImpl implements ReqParamStrategy,ApplicationContextAware {
    private Map<Field, ReqParam>  reqParams = new HashMap<>(); // 存储ReqParam类型的注解
    private Map<Field, Param>  params = new HashMap<>(); // 存储Param类型的注解

    /**
     * 获取字段对应的ReqParam注解
     * @param clazz 类
     * @param field 字段
     * @return 返回ReqParam注解
     */
    public ReqParam getReqParam(Class clazz,Field field){
        // 获取字段对应的getter方法
        String getter = ClassUtil.getGetter(field);
        // 获取getter方法对应的Method对象
        Method getterMethod = ClassUtil.getDeclaredMethod(clazz,getter);
        // 判断getter方法是否为空
        if(getterMethod != null){
            // 判断getter方法是否被ReqParam注解修饰
            ReqParam reqParam = getterMethod.getAnnotation(ReqParam.class);
            if(reqParam != null){
                // 返回getter方法上的ReqParam注解对象
                return reqParam;
            }
        }
        // 判断字段field是否被ReqParam注解修饰
        ReqParam reqParam = ClassUtil.getAnnotation(field, ReqParam.class);
        if(reqParam != null){
            // 返回字段field上的ReqParam注解对象
            return reqParam;
        }
        // 遍历reqParams集合，寻找和field相等的字段对象
        for(Map.Entry<Field, ReqParam> entry : reqParams.entrySet()){
            Field f = entry.getKey();
            // 判断字段名和字段类型是否相等
            if(f.getName().equals(field.getName())&&(f.getType().isAssignableFrom(field.getType())
                    ||field.getType().isAssignableFrom(f.getType()))){
                // 返回找到的字段对应的ReqParam注解对象
                return entry.getValue();
            }
        }

        return null;
    }

    /**
     * 获取字段对应的Param注解
     * @param clazz 类
     * @param field 字段
     * @return 返回Param注解
     */
    @Override
    public Param getParam(Class clazz,Field field) {
        String getter = ClassUtil.getGetter(field);
        // 获取字段的对应的getter方法名

        try {
            Method getterMethod = clazz.getDeclaredMethod(getter);
            // 获取getter方法

            Param param = getterMethod.getAnnotation(Param.class);
            // 获取getter方法的@Param注解

            if(param != null){
                // 如果@Param注解不为空，则直接返回
                return param;
            }
        } catch (NoSuchMethodException e) {
            // 捕获NoSuchMethodException异常，getter方法不存在
            // e.printStackTrace();
        }

        Param param = ClassUtil.getAnnotation(field, Param.class);
        // 获取字段的@Param注解

        if(param != null){
            // 如果@Param注解不为空，则直接返回
            return param;
        }

        for(Map.Entry<Field, Param> entry : params.entrySet()){
            // 遍历params集合中的元素

            Field f = entry.getKey();
            // 获取Map中Key即Field对象

            if(f.getName().equals(field.getName())
                    &&(f.getType().isAssignableFrom(field.getType())||field.getType().isAssignableFrom(f.getType()))){
                // 如果Field对象的名称与要比较的字段名称相同且类型兼容，则返回对应的Param值
                return entry.getValue();
            }
        }

        return null;
    }

    /**
     * 设置ApplicationContext
     * @param applicationContext 上下文对象
     * @throws BeansException 抛出BeansException异常
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        /**
         * 从ApplicationContext中获取所有ReqParamFactory的实例，并将其注册到reqParams和params中
         */
        Map<String, ReqParamFactory> factories = applicationContext.getBeansOfType(ReqParamFactory.class);
        factories.forEach((k,factory)->{
            /**
             * 遍历ReqParamFactory中的reqParamFields，将字段的ReqParam和Param注解添加到reqParams和params中
             */
            factory.getReqParamFields().forEach(field -> {
                ReqParam reqParam = field.getAnnotation(ReqParam.class);
                if(reqParam != null){
                    reqParams.put(field,reqParam);
                }
                Param param = ClassUtil.getAnnotation(field, Param.class);
                if(param !=null){
                    params.put(field,param);
                }
            });
        });
    }
}
